require 'memcache'
class ActiveRecord::Base
  def self.acts_as_xmlcaching
    include ActiveRecord::XmlCaching # if RAILS_ENV == 'production'
  end
end

module ActiveRecord
  module XmlCaching
    @@prefix = ''

    def self.servers=(servers)
      @@servers = servers
    end

    def self.servers
      @@servers
    end

    def self.options=(options)
      @@options = options
    end

    def self.options
      @@options
    end

    def self.prefix=(prefix)
      @@prefix = prefix.to_s
    end

    def self.prefix
      @@prefix
    end

    def self.included(receiver)
      receiver.extend ClassMethods
      receiver.send :include, InstanceMethods
      receiver.class_eval %Q{
        after_destroy :release_memcache
        after_save :release_memcache
        alias :to_xml_without_caching :to_xml
        alias :to_xml :to_xml_with_caching
        alias :to_json_without_caching :to_json
        alias :to_json :to_json_with_caching
      }
      receiver.memcache = MemCache.new(options)
      receiver.memcache.servers = servers
    end

    module ClassMethods

      def memcache=(instance)
        @@memcache = instance
      end

      def memcache
        @@memcache
      end

    end

    module InstanceMethods

      def read_xml_cache(options = {})
        read_cache(:xml, options)
      end

      def read_json_cache(options = {})
        read_cache(:json, options)
      end

      def read_cache(type, options)
        (memcache[cache_id(type)] || {})[cache_key(options)]
      end

      def to_xml_with_caching(options = {})
        if new_record? || options.has_key?(:builder) || !ActionController::Base.perform_caching
          to_xml_without_caching(options)
        else
          xml_cache_id = cache_id(:xml)
          memcache = self.class.memcache
          memcache[xml_cache_id] ||= { }
          key = cache_key(options)
          if memcache[xml_cache_id][key].nil?
            memcache[xml_cache_id] = memcache[xml_cache_id].merge(key => to_xml_without_caching(options))
            logger.debug("id:#{xml_cache_id} set xml cached. => [#{key.inspect}]")
          else
            options[:root]    ||= self.class.to_s.underscore
            options[:except]    = Array(options[:except]) << self.class.inheritance_column unless options[:only] # skip type column
          end
          logger.debug("id:#{xml_cache_id} read xml cache. => [#{key.inspect}]")
          memcache[xml_cache_id][key]
        end
      end

      def to_json_with_caching(options = {})
        if new_record? || !ActionController::Base.perform_caching
          to_json_without_caching(options)
        else
          json_cache_id = cache_id(:json)
          memcache = self.class.memcache
          memcache[json_cache_id] ||= { }
          key = cache_key(options)
          if memcache[json_cache_id][key].nil?
            memcache[json_cache_id] = memcache[json_cache_id].merge(key => to_json_without_caching(options))
            logger.debug("id:#{json_cache_id} set json cached. => [#{key.inspect}]")
          else
            options[:root]    ||= self.class.to_s.underscore
            options[:except]    = Array(options[:except]) << self.class.inheritance_column unless options[:only] # skip type column
          end
          logger.debug("id:#{json_cache_id} read json cache. => [#{key.inspect}]")
          memcache[json_cache_id][key]
        end
      end

      private
      def cache_id(type)
        ActiveRecord::XmlCaching.prefix + "_#{self.class.to_s.downcase}_#{self.id.to_s}_#{type}_#{RAILS_ENV.downcase}"
      end

      def cache_key(args = {})
        args.keys.sort_by { |key|
          key.to_s
        }.collect { |key|
          "#{ key}_#{args[key]}"
        }.compact.join("_")
      end

      protected
      def release_memcache
        xml_cache_id = cache_id(:xml)
        json_cache_id = cache_id(:json)
        unless self.class.memcache[xml_cache_id].nil?
          self.class.memcache[xml_cache_id] = nil
          logger.debug("id:#{xml_cache_id} release xml cached.")
        end
        unless self.class.memcache[json_cache_id].nil?
          self.class.memcache[json_cache_id] = nil
          logger.debug("id:#{json_cache_id} release json cached.")
        end
      end

    end

  end
end
ActiveRecord::XmlCaching.servers = []
ActiveRecord::XmlCaching.options = {}
